﻿#include <stdio.h>
#include <time.h>   //用于获取随机数
#include <windows.h>  //控制dos界面
#include <stdlib.h>  //即standard librany标志库头文件，里面定义了一些宏和通用工具函数
#include <conio.h>   //接收键盘输入输出

#define LEN sizeof(struct snake)


#define U 1
#define D 2
#define L 3
#define R 4

typedef struct snake *Link;
typedef struct snake
{
	int x;
	int y;
	Link next;
}snake;

Link Head, p;
Link food = (Link)malloc(LEN);
int sleeptime = 200;
int HighScore = 0;
int score = 0;
int add = 10;			//食物得分
int endgamestatus = 0;		//游戏结束的情况，1：撞到墙；2：咬到自己；3：主动退出游戏
int status;      //蛇的前进状态

//设置光标位置
void gotoxy(int x, int y) 
{
	COORD c;
	c.X = x;
	c.Y = y;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), c);
}

//设置文字颜色
int color(int c)		
{
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c);
	return 0;
}

//字符画---蛇 
void printfsnake()	
{
	gotoxy(35, 1);
	color(7);
	printf("/^\\/^\\");
	
	gotoxy(34, 2);
	printf("|__| O|");		//蛇眼睛

	gotoxy(33, 2);
	color(2);
	printf("_");

	gotoxy(25, 3);
	color(12);
	printf("\\/");		//蛇信

	gotoxy(31, 3);
	color(2);
	printf("/");

	gotoxy(37, 3);
	color(7);
	printf("\\__/");

	gotoxy(41, 3);
	color(10);
	printf(" \\");

	gotoxy(26, 4);
	color(12);
	printf("\\___");		//舌头

	gotoxy(32, 4);
	printf("_________/");

	gotoxy(31, 4);
	color(2);
	printf("|");

	gotoxy(43, 4);
	color(10);
	printf("\\");

	gotoxy(44, 5);
	color(2);
	printf("\\");

	gotoxy(32, 5);
	color(2);
	printf("\\______");					 //蛇嘴

	gotoxy(39, 6);
	printf("|     |                  \\");     //蛇身

	gotoxy(38, 7);
	printf("/     /                    \\");

	gotoxy(37, 8);
	printf("/     /                      \\\\");

	gotoxy(35, 9);
	printf("/    /                          \\ \\");

	gotoxy(34, 10);
	printf("/    /                            \\  \\");

	gotoxy(33, 11);
	printf("/    /          _----_              \\  \\");

	gotoxy(32, 12);
	printf("/    /        _-~       ~-_          |   |");

	gotoxy(31, 13);
	printf("(     (      _-~    _--_    ~-_      _/   |");

	gotoxy(32, 14);
	printf("\\    ~-____-~   _-~    ~-_    ~-_-~     /");

	gotoxy(33, 15);
	printf("~-_          _-~        ~-_         _-~");

	gotoxy(35, 16);
	printf("~--______-~              ~-____-~");
}

//在文件中读取最高分
void File_out()		
{
	FILE *fp;
	fp = fopen("e:\\save.txt", "a+");
	if (fp == NULL) { printf("can't open this file."); 
	return;
	}
	fscanf(fp, "%d", &HighScore);
	fclose(fp);
}

//储存最高分进文件
void File_in()
{
	FILE *fp;
	fp = fopen("e:\\save.txt", "w+");
	if (fp == NULL) {
		printf("can't open this file.");
		return;
	}
	fprintf(fp, "%d", HighScore);
	fclose(fp);
}

//游戏界面右侧的得分和小提示
void scoreandtips()		
{
	File_out();
	gotoxy(88, 4);
	color(11);
	printf("*最高记录*：%d", HighScore);
	gotoxy(88, 8);
	color(14);
	printf("得分：%d", score);
	gotoxy(95, 11);
	color(13);
	printf("小 提 示");
	gotoxy(84, 13);
	color(6);
	printf("##----------------------------##");
	gotoxy(84, 25);
	printf("##----------------------------##");
	color(3);
	gotoxy(88, 14);
	printf("每个食物得分：%d 分", add);
	gotoxy(88, 16);
	printf("不能穿墙，不能咬到自己");
	gotoxy(88, 18);
	printf("用↑↓←→进行操控");
	gotoxy(88, 20);
	printf("F1 为加速,F2 为减速");
	gotoxy(88, 22);
	printf("space(空格)：暂停游戏");
	gotoxy(88, 24);
	printf("ESC：提出游戏");

}

//绘制地图
void createMap()		
{
	int i, j;
	//边界
	for (i = 4; i < 82; i += 2) {
		color(5);
		gotoxy(i, 2);
		printf("-");
		gotoxy(i,28);
		printf("-");
	}
	for (i = 3; i <28 ; i++) {
		gotoxy(4, i);
		printf("|");
		gotoxy(82, i);
		printf("|");
	}
	//  可行区域
	for (i = 6; i < 80; i += 2)
	{
		for (j = 3; j < 28; j++) {
			gotoxy(i, j);
			color(8);
			printf("口");
		}
	}
}

//初始化蛇身，花蛇身
void initsnake()		
{
	Link tail;  //tail(尾巴)
	int i;
	tail = (Link)malloc(LEN);
	tail->x = 24;
	tail->y = 5;
	tail->next = NULL;
	for (i = 1; i < 5; i++) {
		Head = (Link)malloc(LEN);
		Head->x = 24 + 2 * i;
		Head->y = 5;
		Head->next = tail;
		tail = Head;
	}
	while (tail) {
		gotoxy(tail->x, tail->y);
		color(4);
		printf("*");

		tail = tail->next;
	}
}

//创建并随机出现食物 
void createfood()		
{
	srand((unsigned)time(NULL));		//初始化随机数
	food->x = 1;
	while ((food->x % 2) != 0) {
		food->x = rand() % 74 + 6;    //(6->79)
	}
	food->y = rand() % 25 + 3;     //(3->27)

	p = Head;
	while (p != NULL) {
		if (p->x == food->x && p->y == food->y) {
			createfood();
		}
		p = p->next;
	}
	gotoxy(food->x, food->y);
	color(2);
	printf("*");

}

//判断是否咬到自己
int bitself()		
{
	Link self;
	self = Head->next;
	while (self != NULL) {
		if (self->x == Head->x && self->y == Head->y) {
			return 0;
		}
			self = self->next;
	}
	return 1;
}

//设置蛇撞墙的情况
void cantcrosswall()		
{
	void endgame();
	if (Head->x == 4 || Head->x == 80 || Head->y == 2 || Head->y == 28)
	{
		endgamestatus = 1;
		endgame();
	}
}

//加速
void speedup()		
{
	if (sleeptime >= 50) {
		sleeptime = sleeptime - 10;
		add += 2;
}
}

//减速
void speeddown()		
{
	if (sleeptime < 300) {
		sleeptime = sleeptime + 10;
		add -= 2;
		if (sleeptime == 300)add = 1;
}
}

//控制蛇前进方向
void snakemove()		
{
	void endgame();
	Link p = (Link)malloc(LEN);
	Link l;
	cantcrosswall();
	if (status == U) {
		p->x = Head->x;
		p->y = Head->y-1;
		gotoxy(p->x, p->y);
		color(4);
		printf("*");
		p->next = Head;
		Head = p;
		if (p->x == food->x && p->y == food->y) {			//蛇头
			gotoxy(p->x, p->y);
			color(4);
			printf("*");
		}
		else {
			while (p->next->next != NULL)
				p = p->next;
			if (p->next->x == food->x && p->next->y == food->y) {
				gotoxy(food->x, food->y);
				color(2);
				printf("*");
				score = score + add;
				scoreandtips();
				speedup();
				createfood();
			}
			else {
				gotoxy(p->next->x, p->next->y);
				color(8);
				printf("口");

			}
			l = p->next;
			p->next = NULL;
		}
	}
	if (status == D) {
		p->x = Head->x;
		p->y = Head->y+1;
		gotoxy(p->x, p->y);
		color(4);
		printf("*");

		p->next = Head;
		Head = p;
		if (p->x == food->x && p->y == food->y) {			//蛇头
			gotoxy(p->x, p->y);
			color(4);
			printf("*");
			score = score + add;
			scoreandtips();
			speedup();
			createfood();
		}
		else {
			while (p->next->next != NULL)
				p = p->next;
			if (p->next->x == food->x && p->next->y == food->y) {
				gotoxy(food->x, food->y);
				color(2);
				printf("*");

			}
			else {
				gotoxy(p->next->x, p->next->y);
				color(8);
				printf("口");
			}
			l = p->next;
			p->next = NULL;
		}
	}
	if (status == L) {
		p->x = Head->x-2;
		p->y = Head->y;
		gotoxy(p->x, p->y);
		color(4);
		printf("*");

		p->next = Head;
		Head = p;
		if (p->x == food->x && p->y == food->y) {			//蛇头
			gotoxy(p->x, p->y);
			color(4);
			printf("*");
			score = score + add;
			scoreandtips();
			speedup();
			createfood();
		}
		else {
			while (p->next->next != NULL)
				p = p->next;
			if (p->next->x == food->x && p->next->y == food->y) {
				gotoxy(food->x, food->y);
				color(2);
				printf("*");

			}
			else {
				gotoxy(p->next->x, p->next->y);
				color(8);
				printf("口");
			}
			l = p->next;
			p->next = NULL;
		}
	}
	if (status == R) {
		p->x = Head->x+2;
		p->y = Head->y;
		gotoxy(p->x, p->y);
		color(4);
		printf("*");

		p->next = Head;
		Head = p;
		if (p->x == food->x && p->y == food->y) {			//蛇头
			gotoxy(p->x, p->y);
			color(4);
			printf("*");
			score = score + add;
			scoreandtips();
			speedup();
			createfood();
		}
		else {
			while (p->next->next != NULL)
				p = p->next;
			if (p->next->x == food->x && p->next->y == food->y) {
				gotoxy(food->x, food->y);
				color(2);
				printf("*");
				score = score + add;
				speedup();
				createfood();
			}
			else {
				gotoxy(p->next->x, p->next->y);
				color(8);
				printf("口");
			}
			l = p->next;
			p->next = NULL;
			free(l);
		}
	}
	if (bitself() == 0) {
		endgamestatus = 2;
		endgame();
	}
	cantcrosswall();
}

//控制键盘按键
void keyboradControl()		
{
	void endgame();
	status = R;
	while (1) {
		//GetAsyncKeyState  函数用来判断函数调用时指定虚拟键的态度
		if (GetAsyncKeyState(VK_UP) && status != D) status = U;
		else if (GetAsyncKeyState(VK_DOWN) && status != U) status = D;
		else if (GetAsyncKeyState(VK_LEFT) && status != R) status = L;
		else if (GetAsyncKeyState(VK_RIGHT) && status != L)status = R;
		if (GetAsyncKeyState(VK_SPACE)) {
			while(1){
				Sleep(300);
				if (GetAsyncKeyState(VK_SPACE)) break;
			}
		}
		else if (GetAsyncKeyState(VK_ESCAPE)) {
			endgamestatus = 3;
			endgame();
			break;
		}
		else if (GetAsyncKeyState(VK_F1)) speedup();
		else if (GetAsyncKeyState(VK_F2)) speeddown();
		Sleep(sleeptime);
		snakemove();
	}
}

//游戏结束界面
void Lostdraw()		
{
	int i;
		color(2);
		gotoxy(15, 10);
		printf("O");
		gotoxy(15, 12);
		printf("V");
		gotoxy(15, 14);
		printf("E");
		gotoxy(15, 16);
		printf("R");
		for (i = 30; i <= 90; i++)
	{
		color(4);
		gotoxy(i, 6);
		printf("=");
		gotoxy(i, 20);
		printf("=");
	}
	for (i = 6; i <= 20; i++) 
	{
		color(4);
		gotoxy(30, i);
		printf("|");
		gotoxy(90, i);
		printf("|");
	}

}

//游戏失败之后的选择
void choose()
{
	void welcometogame();
	void endgame();
	int n;
	gotoxy(44,22);
	color(12);
	printf("我要重新玩一局 - 1");
	gotoxy(44, 23);
	printf("返回主清单     - 2");
	gotoxy(44, 24);
	printf("不玩了，退出吧 - 3");
	gotoxy(44, 26);
	color(11);
	printf("选择：");
	scanf("%d", &n);
	switch (n) {
	case 1:
		system("cls"); score = 0; sleeptime = 200;
		add = 10;		createMap();
		scoreandtips();
		initsnake();
		createfood();
		keyboradControl(); break;
	case 2:system("cls"); score = 0; sleeptime = 200;
		add = 10; printfsnake();	scoreandtips();
		welcometogame(); break;
	case 3:exit(0); break;
	default:
		gotoxy(44, 28);
		color(12);
		printf("您的输入有误，请重新输入");
		rewind(stdin);
		getchar();
		system("cls");
		endgame();
	}
}

//游戏结束
void endgame()		
{
	system("cls");
	if (endgamestatus == 1) {
		Lostdraw();
		gotoxy(44, 8);
		color(12);
		printf("对不起，你撞到墙了，游戏结束！");
	}
	if (endgamestatus == 2) {
		Lostdraw();
		gotoxy(44, 8);
		color(12);
		printf("对不起，你咬到自己了，游戏结束！");
	}
	if (endgamestatus == 3) {
		Lostdraw();
		gotoxy(44, 8);
		color(12);
		printf("你已经结束了游戏！");
	}
	Lostdraw();
	gotoxy(48, 12);
	color(13);
	printf("您的得分是 %d\n", score);
	if (score >= HighScore) {
		HighScore = score;
		color(10);
		gotoxy(44, 16);
		printf("创纪录啦！最高分被你刷新了真棒！！！");
			File_in();
	}
	else {
		color(10);
		gotoxy(44, 18);
		printf("继续努力~你离最高分还差:%d", HighScore - score);
	}
	choose();
}

//开始界面
void welcometogame()
{
	void initsnake();
	int n, i, j = 1;
	gotoxy(43, 18);
	color(7);
	printf("贪 吃 蛇 游 戏");
	color(14);
	for (i = 20; i <= 26; i++)
	{
		for (j = 27; j <= 74; j++)
		{
			gotoxy(j, i);
			if (i == 20 || i == 26) {
				printf("-");
			}
			else if (j == 27 || j == 74) {
				printf("|");
			}
		}
	}
	color(12);
	gotoxy(35, 22);
	printf("1.开始游戏");
	gotoxy(35, 23);
	printf("2.退出游戏");
	gotoxy(35, 24);
	printf("请选择<1 2>：[ ]\b\b");		//\b为退格
	color(14);
	scanf("%d", &n);
	switch (n)
	{
	case 1:
		system("cls");
		createMap();
		scoreandtips();
		initsnake();
		createfood();
		break;
	case 2:
		File_in();
		exit(0);
		break;
	default:
		color(12);
		gotoxy(40, 28);
		printf("请输入1~3之间的数！");
		rewind(stdin);
		Sleep(2000);
		system("cls");
		printfsnake();
		scoreandtips();
		welcometogame();
	}
}

int main() {
	system("mode con cols=120 lines=30");		//调整控制台的宽高
	printfsnake();
	File_out();
	scoreandtips();
	welcometogame();
	keyboradControl();
	rewind(stdin);
	getchar();
}